#!/usr/bin/python

import cmd
import ctypes
import numpy
import os
import pyaudio
import pylab
import scipy
import struct
import sys
import threading
import time
import types
import wave

f0 = {
    '1':[697,1209], '2':[697,1336], '3':[697,1477], 'A':[697,1633],
    '4':[770,1209], '5':[770,1336], '6':[770,1477], 'B':[770,1633],
    '7':[852,1209], '8':[852,1336], '9':[852,1477], 'C':[852,1633],
    '*':[941,1209], '0':[941,1336], '#':[941,1477], 'D':[941,1633]
} 

SAMPLE_RATE = 44100
FREQ        = 2000
STRENGTH    = 800
QUIT        = False


class Tone:
    '''
        An iterator that returns a single tone data of in_hz sampled at 44.1KHz 
    '''
    def __init__(self, in_hz):
        self.freq = in_hz
        self.index = 0
        self.Ts = 1.0/SAMPLE_RATE
        
    def __iter__(self):
        return self

    def next(self):
        data = 0
        #for i in self.freq:
        #    data += pylab.sin(2 * pylab.pi * i * self.index * self.Ts)
        data =  pylab.sin(2 * pylab.pi * self.freq * self.index * self.Ts)
        self.index += 1
        return data

def get_dtmf_tone(in_key, out_data):
    '''
        get DTMF tone from key

    1       2           3      A      | 697
    4       5           6      B      | 770
    7       8           9      C      | 852
    *       0           #      D      | 941
--------------------------------------+-----
    1209    1336        1477   1633   |
    '''
    index = 0;
    tone_low  = Tone(f0[in_key][0])
    tone_high = Tone(f0[in_key][1])
    length = len(out_data)
    for i in range(length):
        out_data[index] = tone_high.next() + tone_low.next()
        index += 1

def test():
    pylab.figure()
    points = range(SAMPLE_RATE)
    for i in ['1','2','3','4','5','6','7','8','9','0','*','#','A','B','C','D']:
        get_dtmf_tone(i, points)
        #pylab.plot(points)
        freq=numpy.abs(pylab.fft(points))
        pylab.plot(freq)
        #sys.stdin.readline()

def test_record():

    chunk = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = SAMPLE_RATE
    RECORD_SECONDS = 5
    WAVE_OUTPUT_FILENAME = "output.wav"

    p = pyaudio.PyAudio()

    stream = p.open(format = FORMAT,
                    channels = CHANNELS,
                    rate = RATE,
                    input = True,
                    frames_per_buffer = chunk)

    print "* recording"
    all = []
    for i in range(0, RATE / chunk * RECORD_SECONDS):
        data = stream.read(chunk)
        all.append(data)
    print "* done recording"
    stream.close()
    p.terminate()

    all_val = []
    for i in range(0, RATE / chunk * RECORD_SECONDS):
        for j in range(chunk):
            if j < len(all[i]):
                print 'byte is ', byte
                val = struct.unpack('h', all[i][j*2:j*2+2])[0]
                all_val.append(val)

    pylab.figure()
    pylab.plot(all_val)
    freq=numpy.abs(pylab.fft(all_val))
    pylab.figure()
    pylab.plot(freq)


def test_file():
    chunk = 1024
    wf = wave.open('output.wav', 'rb')
    all = []
    #print wf.getparams()

    for i in range(wf.getnframes()-1):
        data = struct.unpack('h',wf.readframes(1))[0]
        all.append(data)
    wf.close()
    pylab.figure()
    pylab.plot(all)
    freq=numpy.abs(pylab.fft(all))
    pylab.figure()
    pylab.plot(freq)
    #print all
    
class play_sound(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self): 
        block_size = 8192
        p = pyaudio.PyAudio() 
        stream = p.open(format = p.get_format_from_width(2), channels = 1, rate = SAMPLE_RATE, output = True)

        mytone = Tone(FREQ)

        b = ctypes.create_string_buffer(block_size*2)

        try:
            while True:
                for i in range(block_size):
                    data = mytone.next()*STRENGTH
                    struct.pack_into('h', b, i*2, data) 
                stream.write(b)
                mytone.freq = FREQ
                global QUIT
                if QUIT:
                    break

        except KeyboardInterrupt:
            print 'KeyboardInterrupt received while playing, exiting...'

        stream.close()
        p.terminate()


class Mycmd(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        self.prompt = '$'
        self.playing = False
    def do_start(self, args):
        print 'args is ', args, 'length of args is ', len(args)
        pass
    def do_quit(self, args):
        print 'Bye!'
        os._exit(3)
    def do_play(self, args):
        if not self.playing:
            pass
        else:
            print 'already playing...'
            return
        self.playing = True
        global t
        t = play_sound()
        t.start()
        self.playing = False
    def do_stop(self, args):
        global QUIT
        QUIT = True
        global t
        try:
            t.join()
        except NameError:
            pass
    def do_exit(self, args):
        global QUIT
        QUIT = True
        global t
        try:
            t.join()
        except NameError:
            pass
        print 'Bye!'
        os._exit(3)
    def do_h(self, in_val=1):
        global FREQ
        if in_val=='':
            in_val = '1'
        FREQ += float(in_val)
    def do_l(self, in_val=1):
        global FREQ
        if in_val=='':
            in_val = '1'
        FREQ -= float(in_val)
    def do_s(self, in_val=1):
        global STRENGTH
        if in_val=='':
            in_val = '1'
        STRENGTH += float(in_val)
    def do_w(iself, in_val=1):
        global STRENGTH
        if in_val=='':
            in_val = '1'
        STRENGTH -= float(in_val)
    def do_show(self, args):
        print 'freq is ', FREQ, ' strength is ', STRENGTH

def main():
    intro = '''
        Ctrl+c to interrupt
        exit to exit
        help for help
    '''
    mycmd = Mycmd()
    mycmd.cmdloop(intro)    

    
if __name__=='__main__':
    #test()
    #test_file()
    #test_record()
    #pylab.show()
    try:
        main()
    except KeyboardInterrupt: 
        print 'Keyboard interrupt received in main loop, exiting ...'
